package com.haier.os.security.core.validate.code;

import com.haier.os.security.core.properties.SecurityConstants;
import com.haier.os.security.core.properties.SecurityProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
@Slf4j
@Component
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
    /**
     * 失败处理器
     */
    @Autowired
    private AuthenticationFailureHandler haierFailureHandler;
    /**
     * 系统配置信息
     */
    @Autowired
    private SecurityProperties securityProperties;
    /**
     * 系统中的校验码处理器
     */
    @Autowired
    private ValidateCodeProcessorHolder validateCodeProcessorHolder;
    /**
     * 存放所有配置的需要校验验证码的url
     */
    private Map<String, ValidateCodeType> configUrls = new HashMap<>();
    /**
     * 验证请求URL和配置的URL是否匹配的工具类
     */
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     * 初始化需要拦截的URL配置信息
     * @throws ServletException
     */
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();

        configUrls.put(SecurityConstants.DEFAULT_LOGIN_URL_FORM, ValidateCodeType.IMAGE);
        addUrlToMap(securityProperties.getCode().getImage().getUrl(), ValidateCodeType.IMAGE);

        configUrls.put(SecurityConstants.DEFAULT_LOGIN_URL_MOBILE, ValidateCodeType.SMS);
        addUrlToMap(securityProperties.getCode().getSms().getUrl(), ValidateCodeType.SMS);
    }

    /**
     * 将系统中配置的需要校验验证码的url，根据验证码的类型放入对应的map中
     * @param configUrl 配置文件中配置的需要校验验证码的url
     * @param type 类型
     */
    protected void addUrlToMap(String configUrl, ValidateCodeType type) {
        if (StringUtils.isNotBlank(configUrl)) {
            String[] urls = StringUtils.splitByWholeSeparator(configUrl, ",");
            for (String url : urls) {
                configUrls.put(url, type);
            }
        }

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        ValidateCodeType type = getValidateCodeType(request);

        if (null != type) {
            log.info("校验请求[{}]中的验证码，验证码的类型为[{}]", request.getRequestURI(), type);
            try {
                validateCodeProcessorHolder.findValidateCodeProcessor(type)
                        .validate(new ServletWebRequest(request, response));
                log.info("验证码校验通过");
            }catch (ValidateCodeException e) {
                haierFailureHandler.onAuthenticationFailure(request, response, e);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 获取校验码的类型，如果当前请求不需要校验，返回nul
     * @param request
     * @return
     */
    private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
        ValidateCodeType type = null;
        Set<String> urls = configUrls.keySet();
        for (String url : urls ) {
            if (antPathMatcher.match(url, request.getRequestURI())) {
                type = configUrls.get(url);
                break;
            }
        }
        return type;
    }
}
